"""
节假日规则引擎
根据holiday_rules表进行节假日判断
"""

import logging
from datetime import date, timedelta
from typing import List, Optional, Dict, Any, Union
from sqlalchemy.orm import Session
from sqlalchemy import text
from backend.config.database import get_db_session
from backend.entities.holiday import HolidayRules

logger = logging.getLogger(__name__)


def _get_int_value(value: Any) -> Optional[int]:
    """安全地将值转换为整数"""
    if value is None:
        return None
    try:
        return int(value)
    except (ValueError, TypeError):
        logger.warning(f"无法将值转换为整数: {value}")
        return None


class HolidayRuleEngine:
    """节假日规则引擎"""
    
    def __init__(self):
        self._rules_cache = None
        self._cache_timestamp = None
        self._easter_cache = {}  # 复活节日期缓存
    
    def _load_rules(self, db: Session) -> List[HolidayRules]:
        """加载节假日规则"""
        try:
            rules = db.query(HolidayRules).filter(
                HolidayRules.is_active == True
            ).all()
            logger.info(f"加载了 {len(rules)} 条节假日规则")
            return rules
        except Exception as e:
            logger.error(f"加载节假日规则失败: {e}")
            return []
    
    def is_holiday(self, target_date: date, db: Optional[Session] = None) -> bool:
        """判断指定日期是否为节假日"""
        try:
            if db is None:
                with get_db_session() as db_session:
                    return self._check_holiday_with_db(target_date, db_session)
            else:
                return self._check_holiday_with_db(target_date, db)
        except Exception as e:
            logger.error(f"节假日判断失败 {target_date}: {e}")
            return False
    
    def _check_holiday_with_db(self, target_date: date, db: Session) -> bool:
        """使用数据库连接检查节假日"""
        rules = self._load_rules(db)
        
        for rule in rules:
            if self._check_rule(rule, target_date):
                logger.debug(f"日期 {target_date} 匹配规则: {rule.h_name}")
                return True
        
        return False
    
    def _check_rule(self, rule: HolidayRules, target_date: date) -> bool:
        """检查单个规则"""
        try:
            rule_type = getattr(rule, "rule_type", None)
            if rule_type == 'fixed':
                return self._check_fixed_rule(rule, target_date)
            elif rule_type == 'easter':
                return self._check_easter_rule(rule, target_date)
            elif rule_type == 'relative':
                return self._check_relative_rule(rule, target_date)
            else:
                logger.warning(f"未知的规则类型: {rule.rule_type}")
                return False
        except Exception as e:
            logger.error(f"规则检查失败 {rule.h_name}: {e}")
            return False
    
    def _check_fixed_rule(self, rule: HolidayRules, target_date: date) -> bool:
        """检查固定日期规则"""
        month = _get_int_value(rule.month)
        day = _get_int_value(rule.day)
        
        if month is None or day is None:
            return False
        
        return target_date.month == month and target_date.day == day
    
    def _check_easter_rule(self, rule: HolidayRules, target_date: date) -> bool:
        """检查复活节相关规则"""
        try:
            easter_date = self._calculate_easter(target_date.year)
            week_offset = _get_int_value(rule.week_offset) or 0
            holiday_date = easter_date + timedelta(days=week_offset)
            return target_date == holiday_date
        except Exception as e:
            logger.error(f"复活节规则检查失败: {e}")
            return False
    
    def _check_relative_rule(self, rule: HolidayRules, target_date: date) -> bool:
        """检查相对日期规则（如第N个星期X）"""
        month = _get_int_value(rule.month)
        if month is None:
            return False
        
        if target_date.month != month:
            return False
        
        # 计算指定月份的第N个星期X
        target_weekday = _get_int_value(rule.day_of_week) or 0  # 0=周日, 1=周一, ...
        week_offset = _get_int_value(rule.week_offset) or 0
        
        # 找到该月的第一个指定星期X
        first_day = date(target_date.year, target_date.month, 1)
        days_until_target = (target_weekday - first_day.weekday()) % 7
        first_target_weekday = first_day + timedelta(days=days_until_target)
        
        # 计算第N个指定星期X
        target_weekday_date = first_target_weekday + timedelta(weeks=week_offset-1)
        
        return target_date == target_weekday_date
    
    def _calculate_easter(self, year: int) -> date:
        """计算复活节日期（使用Meeus/Jones/Butcher算法）"""
        if year in self._easter_cache:
            return self._easter_cache[year]
        
        try:
            # Meeus/Jones/Butcher算法
            a = year % 19
            b = year // 100
            c = year % 100
            d = b // 4
            e = b % 4
            f = (b + 8) // 25
            g = (b - f + 1) // 3
            h = (19 * a + b - d - g + 15) % 30
            i = c // 4
            k = c % 4
            l = (32 + 2 * e + 2 * i - h - k) % 7
            m = (a + 11 * h + 22 * l) // 451
            month = (h + l - 7 * m + 114) // 31
            day = ((h + l - 7 * m + 114) % 31) + 1
            
            easter_date = date(year, month, day)
            self._easter_cache[year] = easter_date
            return easter_date
        except Exception as e:
            logger.error(f"复活节计算失败 {year}: {e}")
            # 返回一个默认值
            return date(year, 4, 15)
    
    def get_holiday_name(self, target_date: date, db: Optional[Session] = None) -> Optional[str]:
        """获取指定日期的节假日名称"""
        try:
            if db is None:
                with get_db_session() as db_session:
                    return self._get_holiday_name_with_db(target_date, db_session)
            else:
                return self._get_holiday_name_with_db(target_date, db)
        except Exception as e:
            logger.error(f"获取节假日名称失败 {target_date}: {e}")
            return None
    
    def _get_holiday_name_with_db(self, target_date: date, db: Session) -> Optional[str]:
        """使用数据库连接获取节假日名称"""
        rules = self._load_rules(db)
        
        for rule in rules:
            if self._check_rule(rule, target_date):
                return str(rule.h_name) if rule.h_name is not None else None
        
        return None
    
    def batch_check_holidays(self, date_range: List[date], db: Optional[Session] = None) -> Dict[date, bool]:
        """批量检查节假日状态"""
        results = {}
        
        try:
            if db is None:
                with get_db_session() as db_session:
                    rules = self._load_rules(db_session)
                    for dt in date_range:
                        results[dt] = any(self._check_rule(rule, dt) for rule in rules)
            else:
                rules = self._load_rules(db)
                for dt in date_range:
                    results[dt] = any(self._check_rule(rule, dt) for rule in rules)
        except Exception as e:
            logger.error(f"批量节假日检查失败: {e}")
            # 返回默认值
            for dt in date_range:
                results[dt] = False
        
        return results
    
    def get_holiday_rules_summary(self, db: Optional[Session] = None) -> Dict[str, Any]:
        """获取节假日规则摘要"""
        try:
            if db is None:
                with get_db_session() as db_session:
                    return self._get_rules_summary_with_db(db_session)
            else:
                return self._get_rules_summary_with_db(db)
        except Exception as e:
            logger.error(f"获取规则摘要失败: {e}")
            return {}
    
    def _get_rules_summary_with_db(self, db: Session) -> Dict[str, Any]:
        """使用数据库连接获取规则摘要"""
        rules = self._load_rules(db)
        
        summary = {
            'total_rules': len(rules),
            'rule_types': {},
            'holiday_types': {},
            'active_rules': []
        }
        
        for rule in rules:
            # 统计规则类型
            rule_type = rule.rule_type
            summary['rule_types'][rule_type] = summary['rule_types'].get(rule_type, 0) + 1
            
            # 统计节假日类型
            holiday_type = rule.h_type
            summary['holiday_types'][holiday_type] = summary['holiday_types'].get(holiday_type, 0) + 1
            
            # 活跃规则列表
            summary['active_rules'].append({
                'name': rule.h_name,
                'type': rule.h_type,
                'rule_type': rule.rule_type,
                'description': rule.description
            })
        
        return summary


# 全局实例
holiday_engine = HolidayRuleEngine()


def is_holiday(target_date: date, db: Optional[Session] = None) -> bool:
    """便捷函数：判断是否为节假日"""
    return holiday_engine.is_holiday(target_date, db)


def get_holiday_name(target_date: date, db: Optional[Session] = None) -> Optional[str]:
    """便捷函数：获取节假日名称"""
    return holiday_engine.get_holiday_name(target_date, db)


def batch_check_holidays(date_range: List[date], db: Optional[Session] = None) -> Dict[date, bool]:
    """便捷函数：批量检查节假日"""
    return holiday_engine.batch_check_holidays(date_range, db)    